<?php

namespace maotool\file;

use maotool\exception\Exception;
use maotool\exception\RuntimeException;
use maotool\util\AntPathMatcherUtil;
use maotool\util\ArrayUtil;
use maotool\util\DateUtil;
use maotool\util\IdUtil;
use maotool\util\PathUtil;
use maotool\util\TreeUtil;

class DirUtil
{

    /**
     * 缓存
     * @var array
     */
    public static $cache = [];

    /**
     * 路径分隔符
     * @var string
     */
    public static $DIRECTORY_SEPARATOR = DIRECTORY_SEPARATOR;

    /**
     * 获取目录下的文件和目录
     * @return array
     */
    public static function get($path = __DIR__)
    {
        $path = self::path($path);
        if (isset(self::$cache[$path])
            && self::$cache[$path]["createTime"] == self::createTime($path)
            && self::$cache[$path]["updateTime"] == self::updateTime($path)) {
            ++self::$cache[$path]["readCount"];
            self::$cache[$path]["readTime"] = DateUtil::date();
            return self::$cache[$path]['list'];
        }
        $list = [];
        if (is_dir($path)) {
            $currentDir = opendir($path);
            if ($currentDir) {
                while (($dirItem = readdir($currentDir)) !== false) {
                    $subDir = $path . $dirItem;
                    if (in_array($dirItem, [".", ".."], true)) {
                        continue;
                    } else if (is_dir($subDir)) {
                        $list[] = [
                            "name" => $dirItem,
                            "type" => "dir",
                            "path" => $subDir,
                            "createTime" => self::createTime($subDir),
                            "updateTime" => self::updateTime($subDir),
                        ];
                    } else if (is_file($subDir)) {
                        $list[] = [
                            "name" => $dirItem,
                            "type" => "file",
                            "path" => $subDir,
                            "baseName" => pathinfo($dirItem, PATHINFO_FILENAME),
                            "extension" => pathinfo($dirItem, PATHINFO_EXTENSION),
                            "createTime" => FileUtil::createTime($subDir),
                            "updateTime" => FileUtil::updateTime($subDir),
                        ];
                    }
                }
            }
            closedir($currentDir);
        }
        self::$cache[$path] = [
            'readCount' => 1,
            'readTime' => DateUtil::date(),
            'list' => $list,
            'createTime' => self::createTime($path),
            'updateTime' => self::updateTime($path)
        ];
        return $list;
    }

    /**
     * 创建目录
     * @return void
     */
    public static function create($path, $permissions = 0777, $recursive = true)
    {
        $path = self::path($path);
        if (!is_dir($path)) {
            mkdir($path, $permissions, $recursive);
        }
    }

    /**
     * 修改目录权限
     * @return void
     */
    public static function chmod($path, $permissions = 0777)
    {
        $path = self::path($path);
        chmod($path, $permissions);
    }

    /**
     * 删除目录
     * @return void
     */
    public static function delete($path)
    {
        $path = self::path($path);
        if (file_exists($path) && is_dir($path)) {
            $files = self::files($path);
            foreach ($files as $file) {
                FileUtil::delete($file["path"]);
            }
            $dirs = self::dirs($path);
            $dirs = ArrayUtil::sort($dirs, SORT_DESC, "level");
            foreach ($dirs as $dir) {
                rmdir($dir["path"]);
            }
            rmdir(realpath($path));
        }
    }

    /**
     * 获取目录树
     * @return array
     */
    public static function trees($path = __DIR__)
    {
        $path = self::path($path);
        $treeHandle = function ($path, $basePath, $level, $treeHandle) {
            $tree = [];
            $list = self::get($path);
            foreach ($list as $item) {
                if ($item["type"] == "dir") {
                    $item["children"] = $treeHandle($item["path"], $basePath, $level + 1, $treeHandle);
                }
                $item["level"] = $level + 1;
                $item["relativePath"] = substr($item["path"], strlen($basePath));
                $tree[] = $item;
            }
            return $tree;
        };
        return $treeHandle($path, $path, 0, $treeHandle);
    }

    /**
     * 获取目录下所有内容
     * @return array
     */
    public static function lists($path = __DIR__)
    {
        $path = self::path($path);
        return TreeUtil::treeToList(self::trees($path));
    }

    /**
     * 获取目录下面所有文件
     * @param $path
     * @return array
     */
    public static function files($path = __DIR__)
    {
        $path = self::path($path);
        $newList = [];
        $list = TreeUtil::treeToList(self::trees($path));
        foreach ($list as $item) {
            if ($item["type"] == "file") {
                $newList[] = $item;
            }
        }
        return $newList;
    }

    /**
     * 搜索结果列表
     * @return array
     */
    public static function scanLists($path)
    {
        $path = PathUtil::path($path);
        preg_match("/^([^*?\[\]{}])*/", $path, $root);
        $root = DirUtil::path($root[0]);
        $lists = self::lists($root);
        $results = [];
        foreach ($lists as $item) {
            if (AntPathMatcherUtil::match($path, $item["path"])) {
                $results[] = $item;
            }
        }
        return $results;
    }

    /**
     * 搜索结果目录
     * @return array
     */
    public static function scanDirs($path)
    {
        $newList = [];
        $lists = self::scanLists($path);
        foreach ($lists as $item) {
            if ($item["type"] == "dir") {
                $newList[] = $item;
            }
        }
        return $newList;
    }

    /**
     * 搜索结果文件
     * @return array
     */
    public static function scanFiles($path)
    {
        $newList = [];
        $lists = self::scanLists($path);
        foreach ($lists as $item) {
            if ($item["type"] == "file") {
                $newList[] = $item;
            }
        }
        return $newList;
    }

    /**
     * 获取目录下面所有文件
     * @return array
     */
    public static function dirs($path = __DIR__)
    {
        $path = self::path($path);
        $newList = [];
        $list = TreeUtil::treeToList(self::trees($path));
        foreach ($list as $item) {
            if ($item["type"] == "dir") {
                $newList[] = $item;
            }
        }
        return $newList;
    }

    /**
     * 获取目录的md5
     * @return string
     */
    public static function md5($path = __DIR__)
    {
        $path = self::path($path);
        $newList = [];
        $list = self::lists($path);
        foreach ($list as $item) {
            if ($item["type"] == "file") {
                if (is_file($item["path"])) {
                    $newList[] = md5_file($item["path"]);
                } else {
                    $newList[] = md5("none");
                }
            } else {
                $newList[] = $item["path"];
            }
        }
        return md5(implode(';', $newList));
    }

    /**
     * 创建时间
     * @return string
     */
    public static function createTime($path = __DIR__)
    {
        $path = self::path($path);
        return date('Y-m-d H:i:s', filectime($path));
    }

    /**
     * 修改时间
     * @return string
     */
    public static function updateTime($path = __DIR__)
    {
        $path = self::path($path);
        return date('Y-m-d H:i:s', filemtime($path));
    }

    /**
     * 最小文件或目录创建或修改时间
     * @return string
     */
    public static function minTime($path)
    {
        $path = self::path($path);
        $newList = [filectime($path), filemtime($path)];
        $list = self::lists($path);
        foreach ($list as $item) {
            $newList[] = filectime($item["path"]);
            $newList[] = filemtime($item["path"]);
        }
        return date('Y-m-d H:i:s', min($newList));
    }

    /**
     * 最小文件或目录创建或修改时间
     * @return string
     */
    public static function maxTime($path)
    {
        $path = self::path($path);
        $newList = [filectime($path), filemtime($path)];
        $list = self::lists($path);
        foreach ($list as $item) {
            $newList[] = filectime($item["path"]);
            $newList[] = filemtime($item["path"]);
        }
        return date('Y-m-d H:i:s', max($newList));
    }

    /**
     * 拷贝目录
     * @param $originalPath
     * @param $targetPath
     * @return void
     */
    public static function copy($originalPath, $targetPath)
    {
        $originalPath = self::path($originalPath);
        $targetPath = self::path($targetPath);
        if (file_exists($targetPath) && is_dir($targetPath)) {
            throw new RuntimeException($targetPath . "目录已经存在");
        }
        // 创建目录
        $dirs = self::dirs($originalPath);
        self::create($targetPath);
        foreach ($dirs as $dir) {
            self::create($targetPath . $dir["relativePath"]);
        }
        // 创建文件
        $files = self::files($originalPath);
        foreach ($files as $file) {
            FileUtil::copy($originalPath . $file["relativePath"], $targetPath . $file["relativePath"]);
        }
    }

    /**
     * 获取系统的临时目录
     * @return string
     */
    public static function getSystemTemp()
    {
        return sys_get_temp_dir();
    }

    /**
     * 创建系统的临时目录
     * @return string
     */
    public static function createSystemTemp($name = null)
    {
        if (is_null($name)) {
            $name = IdUtil::simpleUUID();
        }
        $path = self::getSystemTemp() . DirUtil::$DIRECTORY_SEPARATOR . $name . DirUtil::$DIRECTORY_SEPARATOR;
        self::create($path);
        return $path;
    }

    /**
     * 路径
     * @param ...$path
     * @return array|string|string[]
     */
    public static function path(...$path)
    {
        $path = PathUtil::path(...$path);
        if (substr($path, -1) !== DirUtil::$DIRECTORY_SEPARATOR) {
            $path = $path . DirUtil::$DIRECTORY_SEPARATOR;
        }
        return $path;
    }

    /**
     * 绝对地址
     * @param ...$path
     * @return false|string
     */
    public static function absolute(...$path)
    {
        $path = self::path(...$path);
        return realpath($path);
    }

    /**
     * 是否存在
     * @param $path
     * @return bool
     */
    public static function exist($path)
    {
        $path = self::path($path);
        return file_exists($path) && is_dir($path);
    }

    /**
     * 是否不存在
     * @param $path
     * @return bool
     */
    public static function notExist($path)
    {
        return !self::exist($path);
    }

    /**
     * 目录名称
     * @return string
     */
    public static function name($path)
    {
        $path = self::path($path);
        return basename($path);
    }

    /**
     * 目录名称
     * @return string
     */
    public static function dirName($path)
    {
        return self::path(dirname($path));
    }

    /**
     * 路径名称
     * @return string
     */
    public static function baseName($path)
    {
        $path = self::path($path);
        return self::path(dirname($path));
    }

}